﻿/**
 * File:   gesture_lock.c
 * Author: AWTK Develop Team
 * Brief:  九宫格手势密码控件。
 *
 * Copyright (c) 2021 - 2021 Guangzhou ZHIYUAN Electronics Co.,Ltd.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * License file for more details.
 *
 */

/**
 * History:
 * ================================================================
 * 2021-02-20 Zhang Zhongji <zhangzhongji@zlg.cn> created
 *
 */


#include "tkc/mem.h"
#include "tkc/utils.h"
#include "gesture_lock.h"

ret_t gesture_lock_set_min_points(widget_t* widget, int32_t min_points) {
  gesture_lock_t* gesture_lock = GESTURE_LOCK(widget);
  return_value_if_fail(gesture_lock != NULL, RET_BAD_PARAMS);

  gesture_lock->min_points = min_points;

  return RET_OK;
}


static ret_t gesture_lock_get_prop(widget_t* widget, const char* name, value_t* v) {
  gesture_lock_t* gesture_lock = GESTURE_LOCK(widget);
  return_value_if_fail(gesture_lock != NULL && name != NULL && v != NULL, RET_BAD_PARAMS);

  if (tk_str_eq(GESTURE_LOCK_PROP_MIN_POINTS, name)) {
     value_set_int32(v, gesture_lock->min_points);
     return RET_OK;
  }

  return RET_NOT_FOUND;
}

static ret_t gesture_lock_set_prop(widget_t* widget, const char* name, const value_t* v) {
  gesture_lock_t* gesture_lock = GESTURE_LOCK(widget);
  return_value_if_fail(widget != NULL && name != NULL && v != NULL, RET_BAD_PARAMS);

  if (tk_str_eq(GESTURE_LOCK_PROP_MIN_POINTS, name)) {
     gesture_lock_set_min_points(widget, value_int32(v));
     return RET_OK;
  }

  return RET_NOT_FOUND;
}

static ret_t gesture_lock_on_destroy(widget_t* widget) {
  gesture_lock_t* gesture_lock = GESTURE_LOCK(widget);
  return_value_if_fail(widget != NULL && gesture_lock != NULL, RET_BAD_PARAMS);



  return RET_OK;
}

// 如果划过的点经过固定基点，则返固定点索引，否则返回-1
static int8_t gesture_lock_point_in_block(gesture_lock_t* gesture_lock, xy_t x, xy_t y) {
  return_value_if_fail(gesture_lock != NULL, -1);
  
  for (int i = 0; i < 9; i++) {
    int32_t delta_x = tk_abs(gesture_lock->base_points[i].x - x);
    int32_t delta_y = tk_abs(gesture_lock->base_points[i].y - y);
    int32_t distance_between_base_point = delta_x * delta_x + delta_y * delta_y;
    if (gesture_lock->r * gesture_lock->r >= distance_between_base_point) {
      return i;
    }
  }

  return -1;
}

static ret_t gesture_lock_draw_lines_to_current_point(gesture_lock_t* gesture_lock, vgcanvas_t* vg) {
  return_value_if_fail(gesture_lock != NULL, RET_BAD_PARAMS);

  vgcanvas_save(vg);
  if (gesture_lock->input_password->size > 0) {
    int input_password_index = 0;
    point_t *begin = darray_get(gesture_lock->input_password, input_password_index++);
    while (gesture_lock->input_password->size > input_password_index) {
      point_t *end = darray_get(gesture_lock->input_password, input_password_index++);

      if (begin != NULL && end != NULL) {
        vgcanvas_begin_path(vg);
        vgcanvas_move_to(vg, begin->x, begin->y);
        vgcanvas_line_to(vg, end->x, end->y);
        vgcanvas_close_path(vg);
        vgcanvas_stroke(vg);
        begin = end;
      }
    }

    if (begin != NULL && gesture_lock->state == PRESSED) {
      vgcanvas_begin_path(vg);
      vgcanvas_move_to(vg, begin->x, begin->y);
      vgcanvas_line_to(vg, gesture_lock->current_position.x, gesture_lock->current_position.y);
      vgcanvas_close_path(vg);
      vgcanvas_stroke(vg);
    }
  }
  
  vgcanvas_restore(vg);
  vgcanvas_flush(vg);

  return RET_OK;
}

static ret_t gesture_lock_draw_block(gesture_lock_t* gesture_lock, vgcanvas_t* vg, float_t x, float_t y, float_t r) {
  return_value_if_fail(vg != NULL, RET_BAD_PARAMS);

  vgcanvas_begin_path(vg);
  //画内圆
  vgcanvas_arc(vg, x, y, r/3.0, 0, 2 * 3.15, FALSE);
  vgcanvas_fill(vg);

  vgcanvas_begin_path(vg);
  //画外圆
  vgcanvas_arc(vg, x, y, r, 0, 2 * 3.15, FALSE);
  vgcanvas_stroke(vg);

  return RET_OK;
}

static ret_t gesture_lock_on_paint_self(widget_t* widget, canvas_t* c) {
  gesture_lock_t* gesture_lock = GESTURE_LOCK(widget);

  vgcanvas_t* vg = canvas_get_vgcanvas(c);
  vgcanvas_save(vg);
  
  color_t color = color_init(0x21, 0x9a, 0xff, 0xff);
  vgcanvas_set_fill_color(vg, color);
  float_t r = tk_min(widget->w, widget->h)/10.0;

  log_debug("gesture_lock->state=%d\n", gesture_lock->state);
  int k = 0;
  for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        if (gesture_lock->state == INIT) {
          color = color_init(0x21, 0x9a, 0xff, 0xff);
        } else if (gesture_lock->state == PRESSED) {
          color = color_init(0x52, 0x49, 0xe7, 0xff);
        } else if (gesture_lock->state == PASS) {
          color = color_init(0x0, 0xff, 0x0, 0xff);
        } else if (gesture_lock->state == FAILED) {
          color = color_init(0xff, 0x0, 0x0, 0xff);
        }

        float_t x = 2*r+j*(3*r);
        float_t y = 2*r+i*(3*r);
        vgcanvas_set_fill_color(vg, color);
        vgcanvas_set_stroke_color(vg, color);
        gesture_lock_draw_block(gesture_lock, vg, x, y, r);

        // 更新控件信息
        gesture_lock->base_points[k].x = 2*r+j*(3*r);
        gesture_lock->base_points[k++].y = 2*r+i*(3*r);
        gesture_lock->r = r;
    }
  }

  // 画路径到当前点
    vgcanvas_set_stroke_color(vg, color);
    gesture_lock_draw_lines_to_current_point(gesture_lock, vg);

  vgcanvas_restore(vg);

  return RET_OK;
}

static bool_t geture_lock_is_password_valid(widget_t* widget) {
  gesture_lock_t* gesture_lock = GESTURE_LOCK(widget);
  return_value_if_fail(gesture_lock != NULL, FALSE);
  
  if (gesture_lock->password->size != gesture_lock->input_password->size) {
    return FALSE;
  }

  for (int i = 0; i < gesture_lock->password->size; i++) {
    point_t* password_iter = (point_t*)darray_get(gesture_lock->password, i);
    point_t* input_password_iter = (point_t*)darray_get(gesture_lock->input_password, i);

    if (password_iter != input_password_iter) {
      return FALSE;
    }
  }
  
  return TRUE;
}

static ret_t gesture_lock_on_pointer_down(widget_t* widget, pointer_event_t* evt) {
  gesture_lock_t* gesture_lock = GESTURE_LOCK(widget);
  return_value_if_fail(gesture_lock != NULL && evt != NULL, RET_BAD_PARAMS);

  gesture_lock->state = PRESSED;
  gesture_lock->current_position.x = evt->x;
  gesture_lock->current_position.y = evt->y;
  darray_clear(gesture_lock->input_password);

  return RET_OK;
}

static ret_t gesture_lock_on_pointer_move(widget_t* widget, pointer_event_t* evt) {
  gesture_lock_t* gesture_lock = GESTURE_LOCK(widget);
  return_value_if_fail(gesture_lock != NULL && evt != NULL, RET_BAD_PARAMS);

  gesture_lock->current_position.x = evt->x;
  gesture_lock->current_position.y = evt->y;

  //保存点，并绘制划过路径
  int8_t base_points_index = gesture_lock_point_in_block(gesture_lock, gesture_lock->current_position.x, gesture_lock->current_position.y);
  if (base_points_index >= 0) {
    if (darray_find(gesture_lock->input_password, &gesture_lock->base_points[base_points_index]) == NULL) {
      darray_push(gesture_lock->input_password, &gesture_lock->base_points[base_points_index]);
    }
  }

  return RET_OK;
}

static ret_t gesture_lock_on_pointer_up(widget_t* widget, pointer_event_t* evt) {
  gesture_lock_t* gesture_lock = GESTURE_LOCK(widget);
  return_value_if_fail(gesture_lock != NULL && evt != NULL, RET_BAD_PARAMS);

  if (gesture_lock->input_password->size < gesture_lock->min_points) {
    gesture_lock->state = FAILED;
    return RET_OK;
  }

  if (gesture_lock->setting_password) {
    //设置密码
    darray_clear(gesture_lock->password);
    for (int i = 0; i < gesture_lock->input_password->size; i++) {
      darray_push(gesture_lock->password, darray_get(gesture_lock->input_password, i));
    }

    gesture_lock->setting_password = FALSE;
    gesture_lock->state = INIT;
  } else {
    //验证密码
    bool_t is_password_valid = geture_lock_is_password_valid(widget);

    if (is_password_valid) {
      gesture_lock->state = PASS;
    } else {
      gesture_lock->state = FAILED;
    }
  }

  return RET_OK;
}

static ret_t gesture_lock_pointer_up_cleanup(widget_t* widget) {
  gesture_lock_t* gesture_lock = GESTURE_LOCK(widget);
  return_value_if_fail(gesture_lock != NULL, RET_BAD_PARAMS);
  widget_ungrab(widget->parent, widget);

  if (gesture_lock->input_password->size == 0) {
    gesture_lock->state = INIT;
  }

  return RET_OK;
}

static ret_t gesture_lock_on_event(widget_t* widget, event_t* e) {
  gesture_lock_t* gesture_lock = GESTURE_LOCK(widget);
  return_value_if_fail(widget != NULL && gesture_lock != NULL, RET_BAD_PARAMS);

  log_debug("e->type=%d\n", e->type);
  switch (e->type) {
    case EVT_POINTER_DOWN: {
      gesture_lock_on_pointer_down(widget, (pointer_event_t*)e);
      widget_grab(widget->parent, widget);
      break;
    }
    case EVT_POINTER_DOWN_ABORT: {
      gesture_lock_pointer_up_cleanup(widget);
      
      break;
    }
    case EVT_POINTER_MOVE: {
      if (gesture_lock->state == PRESSED) {
        gesture_lock_on_pointer_move(widget, (pointer_event_t*)e);
        widget_invalidate(widget, NULL);
      }
      break;
    }
    case EVT_POINTER_UP: {
      gesture_lock_on_pointer_up(widget, (pointer_event_t*)e);
      gesture_lock_pointer_up_cleanup(widget);
      widget_invalidate(widget, NULL);
      break;
    }
    default:
      break;
  }

  return RET_OK;
}

ret_t gesture_lock_reset_password(widget_t* widget) {
  gesture_lock_t* gesture_lock = GESTURE_LOCK(widget);
  return_value_if_fail(widget != NULL && gesture_lock != NULL, RET_BAD_PARAMS);

  gesture_lock->setting_password = TRUE;
  return RET_OK;
}

const char* s_gesture_lock_properties[] = {
  GESTURE_LOCK_PROP_MIN_POINTS,
  NULL
};

TK_DECL_VTABLE(gesture_lock) = {.size = sizeof(gesture_lock_t),
                          .type = WIDGET_TYPE_GESTURE_LOCK,
                          .clone_properties = s_gesture_lock_properties,
                          .persistent_properties = s_gesture_lock_properties,
                          .parent = TK_PARENT_VTABLE(widget),
                          .create = gesture_lock_create,
                          .on_paint_self = gesture_lock_on_paint_self,
                          .set_prop = gesture_lock_set_prop,
                          .get_prop = gesture_lock_get_prop,
                          .on_event = gesture_lock_on_event,
                          .on_destroy = gesture_lock_on_destroy};

widget_t* gesture_lock_create(widget_t* parent, xy_t x, xy_t y, wh_t w, wh_t h) {
  widget_t* widget = widget_create(parent, TK_REF_VTABLE(gesture_lock), x, y, w, h);
  gesture_lock_t* gesture_lock = GESTURE_LOCK(widget);
  return_value_if_fail(gesture_lock != NULL, NULL);

  gesture_lock->min_points = 4;
  gesture_lock->state = INIT;
  gesture_lock->password = darray_create(9, NULL, NULL);
  gesture_lock->input_password = darray_create(9, NULL, NULL);

  return widget;
}

widget_t* gesture_lock_cast(widget_t* widget) {
  return_value_if_fail(WIDGET_IS_INSTANCE_OF(widget, gesture_lock), NULL);

  return widget;
}
